package com.frontalcode 
{
	import flash.net.*;
	import flash.display.*;
	import flash.events.*;
	import flash.utils.*;
	import flash.system.*;
	
	/**
	 * The Asset class is part of Frontal's asset loading and management 
	 * framework. Typically it is not used directly but is rather support for
	 * the img and include tags among other things.
	 */
    public class Asset extends FrontalObject
    {
		static private var nextId : uint = 0;
		
		/**
		 * The Flash Loader associated with this Asset.
		 */
		public var loader : Loader
		
		/**
		 * The document this Asset is associated with. 
		 */
		public var document : Document;
		
		public var rawURL : String;
		
		/**
		 * @private 
		 */
		internal var baseURL : String;
		
		/**
		 * The final URL used for this Asset after any HTTP redirects have been
		 * followed. 
		 */
		public var finalURL : String;
		
		/**
		 * @private 
		 */
		internal var id : uint;
		
		/*
			Variable: state
			
				pending		- The load object is idle.
				loadStarted	- We registered the request to start loading but
							  Flash may not have created any network requests 
							  yet.
				loading		- Are we loading yet? That is, Flash has actually 
							  started requesting the bytes for this object.
				loaded		- Flash has successfully loaded the asset.
				failed		- Flash gave up trying to load the asset.
		*/
		/**
		 * @private 
		 */
		static public var PENDING 		: String = "pending";
		
		/**
		 * @private 
		 */
		static public var LOAD_STARTED 	: String = "loadStarted";
		
		/**
		 * @private 
		 */
		static public var LOADING 		: String = "loading";
		
		/**
		 * @private 
		 */
		static public var LOADED 		: String = "loaded";
		
		/**
		 * @private 
		 */
		static public var FAILED 		: String = "failed";
		
		public var state : String = PENDING;
		
		/*
			Variable: failures
			
				The number of times we have tried and failed to load the asset.
		*/
		/**
		 * @private 
		 */
		internal var failures : Number;
		
		/*
			Variable: startTime
			
				When we started loading.
		*/
		/**
		 * @private 
		 */
		internal var startTime : Number;
		
		/*
			Variable: loadTime
			
				Milliseconds we spent loading.
		*/
		/**
		 * @private 
		 */
		internal var loadTime : Number;

		/**
		 * @private 
		 */
		internal var checkPolicyFile : Boolean;

		public var policyFileRequested : Boolean = false;

		/**
		 * @private 
		 */
		internal var policyFileURL : String;

		/**
		 * @private 
		 */
		internal var loadInCurrentAppAndSecDomains : Boolean;

		/**
		 * Creates a new Asset instance.
		 *
		 * @param document						The document the asset belongs to.
		 * @param url							The string URL of the asset to load.
		 * @param baseURL						Any base URL to prefix the non-absolute URL with.
		 * @param checkPolicyFile				Whether to load any policy file associated 
		 *										with this asset's URL. Needed for bit-level
		 *										access to the loaded data to do, for example,
		 *										image smoothing.
		 * @param policyFileURL					By default, we look for the policy file at the
		 *										root level of the Asset's domain and named
		 *										crossdomain.xml. Use this parameter to 
		 *										override that.
		 * @param loadInCurrentAppAndSecDomains	If true then a context will be created for the
		 *										loader such that the external asset use the
		 *										current application domain and security domain.
		 */
		function Asset ( document : Document, url : String, baseURL : String = null, checkPolicyFile : Boolean = false, policyFileURL : String = null, loadInCurrentAppAndSecDomains : Boolean = false )
		{
			this.document						= document;
			this.rawURL							= url;
			this.baseURL						= baseURL;
			this.checkPolicyFile				= checkPolicyFile;
			this.policyFileURL					= policyFileURL;
			this.loadInCurrentAppAndSecDomains	= loadInCurrentAppAndSecDomains;
			
			id = nextId++;
			
			loader = new Loader ( );
			
			reset ( );
		}
		
		/**
		 * @private 
		 */
		internal function eventHandler ( event : Event ) : void
		{
			switch ( event.type )
			{
				case Event.OPEN:
					state = LOADING;
					startTime = getTimer ( );
					break;
				case IOErrorEvent.IO_ERROR:
					state = FAILED;
					failures++;
					break;
				case ProgressEvent.PROGRESS:
					finalURL = loader.contentLoaderInfo.url;
					if ( checkPolicyFile ) loadPolicyFile ( );
					break;
				case Event.COMPLETE:
					finalURL = loader.contentLoaderInfo.url;
					if ( checkPolicyFile ) loadPolicyFile ( );
					state = LOADED;
					loadTime = getTimer ( ) - startTime;
					break;
				case SecurityErrorEvent.SECURITY_ERROR:
					Debugger.logMessage ( Debugger.ERROR, "Asset", event.toString ( ) );
					break;
			}
		}
		
		/**
		 * @private 
		 */
		internal function startLoad ( ) : void
		{
			state = LOAD_STARTED;

			if ( loadInCurrentAppAndSecDomains )
			{
				try
				{
					var context : LoaderContext = new LoaderContext ( false, new ApplicationDomain ( ApplicationDomain.currentDomain ), SecurityDomain.currentDomain );
					loader.load ( url, context );
				}
				catch ( error:SecurityError )
				{
					// SecurityError: Error #2142: Security sandbox violation: local 
					// SWF files cannot use the LoaderContext.securityDomain 
					// property.
					//
					context = new LoaderContext ( false, new ApplicationDomain ( ApplicationDomain.currentDomain ) );
					loader.load ( url, context );
				}
			}
			else
			{
				loader.load ( url );
			}
			
			// An unload might have blown away our contentLoaderInfo so we add
			// our event listeners every time we do a load.
			//
			for each ( var i : String in [ Event.COMPLETE, IOErrorEvent.IO_ERROR, ProgressEvent.PROGRESS, Event.OPEN, /* Event.UNLOAD, */ SecurityErrorEvent.SECURITY_ERROR ] )
			{
				addListener ( loader.contentLoaderInfo, i, eventHandler );
			}
		}
	
		/*
			Function: fractionComplete
			
				Return a number from 0 to 1 indicating how much we've loaded.
		*/
		public function get fractionComplete ( ) : Number
		{
			return bytesTotal > 0 ? bytesLoaded / bytesTotal : 0;
		}
		
		/*
			Function: bytesLoaded
			
				The number of bytes currently associated with this asset.
		*/
		public function get bytesLoaded ( ) : Number
		{
			return loader.contentLoaderInfo != null ? loader.contentLoaderInfo.bytesLoaded : 0;
		}
		
		/*
			Function: bytesTotal
			
				The number of bytes to be loaded for this asset.
		*/
		public function get bytesTotal ( ) : Number
		{
			return loader.contentLoaderInfo != null ? loader.contentLoaderInfo.bytesTotal : 0;
		}
		
		/*
			Function: doneLoading
			
			Returns:
			
				true if Flash has completed the load process either successfully
				or with a failure.
		*/
		/**
		 * @private 
		 */
		internal function doneLoading ( ) : Boolean
		{
			return state == LOADED || state == FAILED;
		}
		
		/*
			Function: cancelLoad
		*/
		/**
		 * @private 
		 */
		internal function cancelLoad ( ) : void
		{
			state = PENDING;
			try
			{
				loader.close ( );
			}
			catch ( e : Error )
			{
				// Error #2029: This URLStream object does not have a stream opened.
			}
		}
		
		/*
			Function: unload
		*/
		/**
		 * @private 
		 */
		internal function unload ( ) : void
		{
			loader.unload ( );
		}
		
		/*
			Function: url
			
				Return the url prefixed with the base URL if set.
		*/
		public function get url ( ) : URLRequest
		{
			return new URLRequest ( AssetManager.getInstance ( ).getFullURL ( rawURL, baseURL ) );
		}
		
		/*
			Function: reset
			
				Reset the object to load again.
		*/
		/**
		 * @private 
		 */
		internal function reset ( ) : void
		{
			failures = 0;
		}
		
		/*
			Function: loadPolicyFile
			
				This method will load any policy file associated with this 
				Asset's final URL. The final URL is not known until the Asset
				begins downloading so if this call is made prior to that then 
				the request will be delayed until that information is available.
				To avoid any SecurityError exceptions then check that this
				property is true prior to doing any bit-level manipulations:
				
					loader.contentLoaderInfo.childAllowsParent
					
			Notes:
				
				We ignore this call if the Asset is a SWF. This is because SWFs
				use different security mechanisms and so loading the policy file
				would be a waste.
		*/
		public function loadPolicyFile ( ) : void
		{
			if ( loader.contentLoaderInfo.contentType == "application/x-shockwave-flash" ) return;
			
			checkPolicyFile = true;
			
			if ( finalURL != null && ! policyFileRequested )
			{
				policyFileRequested = true;
				var url : String = policyFileURL;
				if ( url == null )
				{
					// Extract the domain from the Asset's final URL and add
					// crossdomain.xml.
					//
					url = finalURL.split ( "/", 3 ).join ( "/" ) + "/crossdomain.xml";
				}
				Security.loadPolicyFile ( url );
			}
		}
	}
}
